﻿using Sunny.UI;
using Sunny.UI.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Timer = System.Threading.Timer;

namespace socketForm
{
    public partial class FrmServer : UIForm
    {
        //服务器socket对象
        public Socket serverSocket;
        //客户端socket列表
        public Dictionary<Socket, string> clientSockets = new Dictionary<Socket, string>();
        //记录各个客户端最后一次通信时间
        public Dictionary<Socket, DateTime> lastRecTimes = new Dictionary<Socket, DateTime>();
        private object clientSocketLock = new object();
        private Timer checkClientActivityTimer;

        public FrmServer()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.txtIP.Text = SocketUtil.getIP();
            listview.Items.Clear();
        }

        private void BindData()
        {
            if (listview.InvokeRequired) //判断是否跨线程访问
            {
                listview.Invoke(new Action(() =>
                {
                    lock (clientSocketLock)
                    {
                        showOnline();
                    }
                }));
            }
            else //本线程内访问
            {
                showOnline();
            }
        }

        private void showOnline()
        {
            listview.Items.Clear();
            foreach (var kvp in clientSockets)
            {
                string ip_port = kvp.Value;
                ListViewItem item = new ListViewItem(ip_port.Split(':')[1]);
                DateTime lastdt = lastRecTimes[kvp.Key];
                if (DateTime.Now - lastdt > TimeSpan.FromSeconds(15))  //10秒发送一次心跳，15秒没收到判断掉线
                {
                    item.ImageIndex = 1;
                }
                else {
                    item.ImageIndex = 0;
                }
                listview.Items.Add(item);
            }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            start();
            btnStart.Enabled = false;
        }

        public void start()
        {
            string ip = txtIP.Text;
            string port = txtPort.Text;
            //1、创建socket
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //2、绑定端口IP
            serverSocket.Bind(new IPEndPoint(IPAddress.Parse(ip), int.Parse(port)));
            //3、开始监听
            serverSocket.Listen(100);//等待连接队列，最多100个等待，超过的返回错误
            //4、开始接受客户端连接
            ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), serverSocket);
            //5、启动定时器，每5秒检查一次客户端活动
            checkClientActivityTimer = new Timer(CheckClientActivity, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));
        }

        /// <summary>
        /// 每5秒检查一次客户端是否断开
        /// </summary>
        /// <param name="state"></param>
        private void CheckClientActivity(object state)
        {
            lock (clientSocketLock)
            {
                List<Socket> disconnectedClients = new List<Socket>();
                foreach (var kvp in clientSockets)
                {
                    Socket socket = kvp.Key;
                    DateTime lastdt = lastRecTimes[socket];
                    if (DateTime.Now - lastdt > TimeSpan.FromSeconds(32))
                    {
                        disconnectedClients.Add(kvp.Key);
                    }
                }
                foreach (var disconnectedClient in disconnectedClients)
                {
                    clientSockets.Remove(disconnectedClient);
                    lastRecTimes.Remove(disconnectedClient);
                }
            }
            BindData();
        }

        public void AcceptClientConnect(object socket)
        {
            while (true)
            {
                Socket proxySocket = null;
                try
                {
                    proxySocket = serverSocket.Accept();//阻塞当前线程，接收客户端的连接
                    string clientInfo = proxySocket.RemoteEndPoint.ToString();
                    lock (clientSocketLock)
                    {
                        clientSockets.Add(proxySocket, clientInfo);
                        lastRecTimes.Add(proxySocket, DateTime.Now);
                    }
                    AppendTextToTxtLog($"{DateTime.Now.ToString("HH:mm:ss")}:客户端已连接");
                    //不停的接收当前连接的客户端发送来的消息
                    ThreadPool.QueueUserWorkItem(ReceiveData, proxySocket);
                }
                catch (Exception ex)
                {
                    if (serverSocket != null && serverSocket.Connected)
                    {
                        // 如果服务器Socket正常且处于连接状态，只是接受客户端连接出现临时问题，继续尝试接受
                        continue;
                    }
                    else
                    {
                        // 服务器Socket出现问题，比如关闭了等情况，跳出循环
                        AppendTextToTxtLog($"{DateTime.Now.ToString("HH:mm:ss")}:服务器监听失败");
                        break;
                    }
                }
            }

        }

        //接收客户端的消息
        public void ReceiveData(object socket)
        {
            byte[] data = new byte[1024 * 1024];
            var proxySocket = socket as Socket;
            string clientInfo = "";
            lock (clientSocketLock)
            {
                clientSockets.TryGetValue(proxySocket, out clientInfo);
            }
            while (true)
            {
                int len = 0;
                try
                {
                    len = proxySocket.Receive(data, 0, data.Length, SocketFlags.None);
                }
                catch (Exception ex)
                {
                    //客户端异常退出:客户端网络问题导致
                    AppendTextToTxtLog($"{DateTime.Now.ToString("HH:mm:ss")}:客户端异常退出");
                    lock (clientSocketLock)
                    {
                        clientSockets.Remove(proxySocket);
                        lastRecTimes.Remove(proxySocket);
                    }
                    // 尝试重新连接客户端
                    ReconnectClient(proxySocket, clientInfo);
                    return; //让方法结束，结束当前接收数据的异步线程
                }

                //接收到0个字节代表客户端正常退出
                if (len <= 0)
                {
                    //客户端正常退出
                    //Console.WriteLine($"客户端正常退出");
                    AppendTextToTxtLog($"{DateTime.Now.ToString("HH:mm:ss")}:客户端正常退出");
                    lock (clientSocketLock)
                    {
                        clientSockets.Remove(proxySocket);
                        lastRecTimes.Remove(proxySocket);
                    }
                    // 尝试重新连接客户端
                    ReconnectClient(proxySocket, clientInfo);
                    return; //让方法结束，结束当前接收数据的异步线程
                }

                //接收数据
                string str = Encoding.UTF8.GetString(data, 0, len);
                lock (clientSocketLock) 
                {
                    if (lastRecTimes.ContainsKey(proxySocket)) //断线重连
                    {
                        lastRecTimes[proxySocket] = DateTime.Now;
                    }
                    else  //首次连接
                    {
                        lastRecTimes.Add(proxySocket, DateTime.Now);
                    }
                }
                AppendTextToTxtLog($"{DateTime.Now.ToString("HH:mm:ss")}接收{clientInfo}:{str}");
            }
        }

        // 尝试重新连接客户端的方法
        private void ReconnectClient(Socket clientSocket, string clientInfo)
        {
            int maxReconnectAttempts = 15; //15次，15秒可重联，超过重新创建对象
            int reconnectInterval = 1000;  // 重连间隔时间（毫秒）
            int reconnectAttempts = 0;
            while (reconnectAttempts < maxReconnectAttempts)
            {
                try
                {
                    // 关闭原客户端Socket（如果还未关闭的话）
                    clientSocket.Close();
                    // 重新创建Socket对象尝试连接客户端
                    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    IPAddress clientIP = IPAddress.Parse(clientInfo.Split(':')[0]);
                    int clientPort = int.Parse(clientInfo.Split(':')[1]);
                    clientSocket.Connect(clientIP, clientPort);
                    lock (clientSocketLock)
                    {
                        clientSockets.Add(clientSocket, clientInfo);
                        lastRecTimes.Add(clientSocket, DateTime.Now);
                    }
                    Console.WriteLine($"客户端 {clientInfo} 重新连接成功");
                    // 重新启动处理该客户端通信的线程
                    ThreadPool.QueueUserWorkItem(ReceiveData, clientSocket);
                    break;
                }
                catch (SocketException ex)
                {
                    reconnectAttempts++;
                    Console.WriteLine($"客户端 {clientInfo} 第 {reconnectAttempts} 次重连失败，异常信息：{ex.Message}");
                    Thread.Sleep(reconnectInterval);
                }
            }
            if (reconnectAttempts == maxReconnectAttempts)
            {
                Console.WriteLine($"客户端 {clientInfo} 重连失败，放弃重连");
            }
        }

        private void stopConnect(Socket proxySocket)
        {
            try
            {
                if (proxySocket.Connected)
                {
                    proxySocket.Shutdown(SocketShutdown.Both);
                    proxySocket.Close(100);

                }
            }
            catch (Exception ex)
            {

            }
        }

        //往日志文本框上添加数据
        public void AppendTextToTxtLog(string txt)
        {
            if (txtLog.InvokeRequired) //判断是否跨线程访问
            {
                //Invoke同步方法      BeginInvoke异步方法
                txtLog.Invoke(new Action<string>(s => {
                    this.txtLog.Text = string.Format("{0}\r\n{1}", s, txtLog.Text);
                }), txt);
            }
            else //本线程内访问
            {
                this.txtLog.Text = string.Format("{0}\r\n{1}", txt, txtLog.Text);
            }
            
        }

        /// <summary>
        /// 发送文字
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            foreach (KeyValuePair<Socket,string> keyValuePair in clientSockets)
            {
                Socket proxySocket = keyValuePair.Key;
                if (proxySocket.Connected)
                {
                    byte[] data = Encoding.UTF8.GetBytes(txtMsg.Text);
                    //添加头部一个字节消息类型 : 1文字    2闪屏    3文件
                    byte[] result  = new byte[data.Length+1];
                    result[0] = 1;
                    Buffer.BlockCopy(data, 0, result, 1, data.Length);
                    proxySocket.Send(result,0,result.Length,SocketFlags.None);
                }
            }
        }

        /// <summary>
        /// 发送闪屏
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendShake_Click(object sender, EventArgs e)
        {
            foreach (KeyValuePair<Socket, string> keyValuePair in clientSockets)
            {
                Socket proxySocket = keyValuePair.Key;
                if (proxySocket.Connected)
                {
                    proxySocket.Send(new byte[]{2}, SocketFlags.None);
                }
            }
        }

        /// <summary>
        /// 发送文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            //读取要发送的文件数据
            using (OpenFileDialog ofd = new OpenFileDialog())
            {
                if (ofd.ShowDialog() != DialogResult.OK) {
                    return;
                }
                //文件数据
                byte[] data = File.ReadAllBytes(ofd.FileName);
                //文件名数据：长度64
                byte[] nameData = new byte[64];
                byte[] temp = Encoding.UTF8.GetBytes(ofd.SafeFileName);
                Buffer.BlockCopy(temp, 0, nameData, 0, temp.Length);
                //数据包：1+64+文件数据字节数
                byte[] result = new byte[1+64+data.Length];
                result[0] = 3;
                Buffer.BlockCopy(nameData, 0, result, 1, nameData.Length);
                Buffer.BlockCopy(data, 0, result, 1+64, data.Length);
                string str = Encoding.UTF8.GetString(result);
                foreach (KeyValuePair<Socket, string> keyValuePair in clientSockets)
                {
                    Socket proxySocket = keyValuePair.Key;
                    if (!proxySocket.Connected)
                    {
                        continue;
                    }
                    proxySocket.Send(result, SocketFlags.None);
                }
            }

        }
    }
}
